﻿using GN.Pay.Client.Reflection.Meta;
using GN.Pay.Client.Utils;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GN.Pay.Client.Notify
{
    /// <summary>
    /// 通知消息
    /// </summary>
    public abstract class NotifyMessage : ServiceMessage
    {
        private readonly IDictionary<string, string> notifyMessages;

        /// <summary>
        /// 实例化
        /// </summary>
        /// <param name="notifyMessages">通知消息</param>
        /// <param name="payConfigure">支付配置</param>
        public NotifyMessage(IDictionary<string, string>
            notifyMessages, IPayConfigure payConfigure)
        {
            this.notifyMessages = ExceptionUtils.CheckNotNull(notifyMessages, nameof(notifyMessages));
            this.PayConfigure = ServiceUtils.CheckPayConfigure(payConfigure);
            LoadProperty();
        }

        /// <summary>
        /// 获取支付配置
        /// </summary>
        public IPayConfigure PayConfigure { get; private set; }

        /// <summary>
        /// 加载属性
        /// </summary>
        private void LoadProperty()
        {
            ISet<MetaProperty> propertys = ReflectionUtils.GetCacheMetaPropertys(GetType());
            foreach (var p in propertys)
            {
                if (p.PropertyInfo.CanWrite && p.IsGeneralTypeMember)
                {
                    string value;
                    if (notifyMessages.TryGetValue(p.PropertyInfo.Name, out value)
                        && !string.IsNullOrWhiteSpace(value))
                    {
                        p.SetMemberValue(this,
                            DataConvertUtils.TargetValueConvert(p.PropertyInfo.PropertyType, value));
                    }
                }
            }
        }

        /// <summary>
        /// 成功消息
        /// </summary>
        /// <returns></returns>
        public string SuccessMessage()
        {
            return "success";
        }

        /// <summary>
        /// 通道Id
        /// </summary>
        public string ChannelId { get; set; }

        /// <summary>
        /// 通道名称
        /// </summary>
        public string ChannelName { get; set; }

        /// <summary>
        /// 失败消息
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public string FailMessage(string message)
        {
            if (message == null)
            {
                message = "fail";
            }
            return message;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <param name="name"></param>
        private void CheckNotifyParam(string value, string name)
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                throw new PayException(Consts.CHECK_SERVER_RESPONSE_ERROR,
                    "服务器响应错误，缺少 " + name + "参数");
            }
        }

        /// <summary>
        /// 转为骆锋命名，即首个字母为小写
        /// </summary>
        /// <param name="value">值</param>
        /// <returns></returns>
        private string ToCamelCase(string value)
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                return value;
            }
            if (value.Length == 1)
            {
                return value.ToLower(CultureInfo.InvariantCulture);
            }
            return char.ToLower(value[0], CultureInfo.InvariantCulture) + value.Substring(1);
        }

        /// <summary>
        /// 签名验证
        /// </summary>
        /// <returns></returns>
        public bool SginVerify()
        {
            CheckNotifyParam(AppId, nameof(AppId));
            CheckNotifyParam(MerchantId, nameof(MerchantId));
            CheckNotifyParam(RandomStr, nameof(RandomStr));
            CheckNotifyParam(Sign, nameof(Sign));
            if (Math.Abs(TimeStamp - DateTimeUtils.CurrentUtcTimeStampMilliseconds) > Consts.SERVER_RESPONSE_TIMESTAMP_DIFF_MILLISECONDS)
            {
                throw new PayException(Consts.SERVER_RESPONSE_TIMESTAMP_DIFF_MILLISECONDS,
                    "服务器通知错误，TimeStamp 大于限定范围");
            }
            SignDictionary signDic = new SignDictionary();
            foreach (var kv in notifyMessages)
            {
                if (!string.IsNullOrWhiteSpace(kv.Key)
                    && !string.IsNullOrWhiteSpace(kv.Value) 
                    && !kv.Key.Equals("sign", StringComparison.InvariantCultureIgnoreCase))
                {
                    signDic[ToCamelCase(kv.Key)] = kv.Value;
                }
            }
            string content = signDic.UrlLinkString() + "&key=" + this.PayConfigure.AppKey;
            return Md5Utils.SginVerify(content, Sign, Encoding.UTF8);
        }
    }
}
